#include <QMetaObject>
#include <QList>
#include <QMap>
#include <QQmlContext>
#include <qqml.h>
#include <private/qobject_p.h>

#include "basepropertybinder.h"



struct BasePropertyBinder::Data{

    ~Data(){
        for(auto i : connections)
        {
            QObject::disconnect(i);
        }
    }

    QList<QMetaObject::Connection> connections;
};

class IdBinder_Property_Watcher:public QtPrivate::QSlotObjectBase
{
public:
    IdBinder_Property_Watcher():QtPrivate::QSlotObjectBase(&IdBinder_Property_Watcher::FuncImpl){

    }


    static void FuncImpl(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret){
        auto This = static_cast<IdBinder_Property_Watcher*>(this_);
        if(which == QtPrivate::QSlotObjectBase::Operation::Call)
        {
            if(!This->origin->enabled())
            {
                return;
            }
            auto val = This->src_prop.read(This->src);
            This->dst_prop.write(This->dst,val);
        }
        else if(which == QtPrivate::QSlotObjectBase::Compare)
        {
            *ret = false;
        }
        else if(which == QtPrivate::QSlotObjectBase::Destroy)
        {
            delete This;
        }



    }

    QMetaProperty src_prop;
    QObject* src;
    QMetaProperty dst_prop;
    QObject* dst;
    BasePropertyBinder* origin;
};

BasePropertyBinder::BasePropertyBinder(QObject *parent):QObject(parent)
{

}

BasePropertyBinder::~BasePropertyBinder()
{

}

QObject *BasePropertyBinder::source() const
{
    return m_source;
}

void BasePropertyBinder::setSource(QObject *newSource)
{
    if (m_source == newSource)
        return;
    m_source = newSource;
    update();
    emit sourceChanged();
}

QObject *BasePropertyBinder::target() const
{
    return m_target;
}

void BasePropertyBinder::setTarget(QObject *newTarget)
{
    if (m_target == newTarget)
        return;
    m_target = newTarget;
    update();
    emit targetChanged();
}

static QList<QMetaObject::Connection> bindProperty(BasePropertyBinder* origin,const BasePropertyBinder::BindDataList& bindList){
    QList<QMetaObject::Connection> ret;
    for(const auto & i : bindList)
    {
        if(!i.srcProp.hasNotifySignal())
        {
            continue;
        }
        auto slotObj = new IdBinder_Property_Watcher();
        slotObj->dst = i.dst;
        slotObj->dst_prop = i.dstProp;
        slotObj->src = i.src;
        slotObj->src_prop = i.srcProp;
        slotObj->origin = origin;

        ret << QObjectPrivate::connect(i.src,i.srcProp.notifySignalIndex(),i.dst,slotObj,Qt::AutoConnection);
    }

    return ret;
}

void BasePropertyBinder::update()
{
    data.reset();

    if(!m_target || !m_source)
        return;

    data.reset(new Data());

    auto blist = generateBindList();
    data->connections = bindProperty(this,blist);
//    auto sourceMap = UtilsPrivate::groupById(m_source);
//    auto targetMap = UtilsPrivate::groupById(m_target);

//    for(auto i:sourceMap.keys())
//    {
//        if(targetMap.contains(i))
//        {
//            bindProperty(sourceMap[i],targetMap[i],m_propertys,data->connections);
//        }
    //    }
}

bool BasePropertyBinder::enabled() const
{
    return m_enabled;
}

void BasePropertyBinder::setEnabled(bool newEnabled)
{
    if (m_enabled == newEnabled)
        return;
    m_enabled = newEnabled;
    emit enabledChanged();
}
